/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021.
 * Description: Provide a proc interface for displaying idle time.
 * Author:xiechengliang
 * Create: 2021-03-30
 */

#include <linux/cpumask.h>
#include <linux/fs.h>
#include <linux/kernel_stat.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/time.h>
#include <linux/rtos_idle.h>

#define SIZE_PER_CPU 128

static void print_percpu_idle(struct seq_file *p, const u64 busy, const u64 idle)
{
	seq_put_decimal_ull(p, " ", nsec_to_clock_t(busy));
	seq_put_decimal_ull(p, " ", 0);
	seq_put_decimal_ull(p, " ", 0);
	seq_put_decimal_ull(p, " ", nsec_to_clock_t(idle));
	seq_put_decimal_ull(p, " ", 0);
	seq_put_decimal_ull(p, " ", 0);
	seq_put_decimal_ull(p, " ", 0);
	seq_put_decimal_ull(p, " ", 0);
	seq_put_decimal_ull(p, " ", 0);
	seq_put_decimal_ull(p, " ", 0);
	seq_putc(p, '\n');
}

static int show_idle(struct seq_file *p, void *v)
{
	int i;
	u64 idle = 0;
	ktime_t now;
	u64 now_u64;

	for_each_online_cpu(i)
		idle += rtos_get_idle_time(i);

	/* The output format is consistent with /proc/stat, which is convenient for product use */
	seq_puts(p, "cpu ");
	print_percpu_idle(p, 0, idle);

	for_each_online_cpu(i) {
		idle = rtos_get_idle_time(i);
		now = ktime_get();
		now_u64 = ktime_to_us(now);
		now_u64 = now_u64 * NSEC_PER_USEC;

		seq_printf(p, "cpu%d", i);
		print_percpu_idle(p, now_u64 - idle, idle);
	}

	return 0;
}

static int idle_open(struct inode *inode, struct file *file)
{
	/* Each cpu uses 128 bytes to save data, first line display total idle time */
	size_t size = SIZE_PER_CPU * (num_online_cpus() + 1);

	return single_open_size(file, show_idle, NULL, size);
}

static const struct proc_ops proc_idle_operations = {
	.proc_open		= idle_open,
	.proc_read_iter		= seq_read_iter,
	.proc_lseek		= seq_lseek,
	.proc_release		= single_release,
};

static int __init proc_idle_init(void)
{
	proc_create("rtos-idle", 0444, NULL, &proc_idle_operations);
	return 0;
}
fs_initcall(proc_idle_init);
